﻿// Copyright 2016 Intel Corporation.

// The source code, information and material ("Material") contained herein is
// owned by Intel Corporation or its suppliers or licensors, and title to such
// Material remains with Intel Corporation or its suppliers or licensors. The
// Material contains proprietary information of Intel or its suppliers and
// licensors. The Material is protected by worldwide copyright laws and treaty
// provisions. No part of the Material may be used, copied, reproduced, modified,
// published, uploaded, posted, transmitted, distributed or disclosed in any way
// without Intel's prior express written permission. No license under any patent,
// copyright or other intellectual property rights in the Material is granted to
// or conferred upon you, either expressly, by implication, inducement, estoppel
// or otherwise. Any license under such intellectual property rights must be
// express and approved by Intel in writing.

// Include any supplier copyright notices as supplier requires Intel to use.

// Include supplier trademarks or logos as supplier requires Intel to use,
// preceded by an asterisk. An asterisked footnote can be added as follows:
// *Third Party trademarks are the property of their respective owners.

// Unless otherwise agreed by Intel in writing, you may not remove or alter this
// notice or any other notice embedded in Materials by Intel or Intel's suppliers
// or licensors in any way.

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.IO;
using System.Windows.Interop;
using System.Security;
using System.Runtime.InteropServices;
using System.Threading;

using ExtensionMethods;
using PasswordManager;

namespace Password_manager
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        public static List<PasswordItemDetails> itemDetails = new List<PasswordItemDetails>();
        String working_vault;
        PasswordManagerCore mgr = new PasswordManagerCore();
        bool bCreateNewVault = false;
        UserPrefs prefs = new UserPrefs();
        HwndSource hsrc;

        public enum PasswordDisplayStatus
        {
            BadFile = 10,
            CantUpdate = 15,
            Clipboard = 14,
            Exists = 5,
            IncorrectVersion = 9,
            Invalid = 8,
            MemoryAllocation = 2,
            NoChange = 1,
            NoPermission = 6,
            NotFound = 7,
            OK = 0,
            PasswordIncorrect = 13,
            RandomGenerator = 11,
            Range = 4,
            Size = 3,
            UserCancelled = 12,
            WriteFailed = 16,
        }

        public PasswordManagerCore PasswordManager () { return this.mgr; }

        public MainWindow()
        {
            FeatureSupport sgxfeature = new FeatureSupport();
            InitializeComponent();

            // In the SGX branch, the lock timeout is also stored in our password vault. 
            mgr.set_lock_timeout((ushort)prefs.lockDelay);

            // Detect SGX support
            if ( sgxfeature.is_supported() == 1 )
            {
                // We support SGX, but are we enabled or is further action required?
                if (sgxfeature.is_enabled() == 1)
                {
                    mgr.set_sgx_support();
                }
                else if (sgxfeature.reboot_required() == 1)
                {
                    MessageBox.Show("Intel­® SGX is supported on this system, but a reboot is required to enable it. This application will run without Intel® SGX support for now.", "Intel® SGX not enabled");
                } else if ( sgxfeature.bios_enable_required() == 1 )
                {
                    MessageBox.Show("Intel® SGX is supported on this system, but it needs to be manually enabled in the BIOS. This application will run without Intel® SGX for now.", "Intel® SGX not enabled");
                }
            }
        }


        private void Exit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();

        }

        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

            if (lbPasswordList.SelectedItem != null)
            {
                this.txtAccount.Text = (lbPasswordList.SelectedItem as PasswordItemDetails).Account;
                this.txtURL.Text = (lbPasswordList.SelectedItem as PasswordItemDetails).URLstring;
                this.txtLogin.Text = (lbPasswordList.SelectedItem as PasswordItemDetails).Login;
            }

        }


        public static string returnErrorcode(int rv)
        {

            PasswordDisplayStatus passDisplayStatus = (PasswordDisplayStatus)rv;
            string stringValue = passDisplayStatus.ToString();
            return stringValue;
        }

        private void unlock_Click(object sender, RoutedEventArgs e)
        {
            uint count = 0;
            SecureString randpass = new SecureString();
            SecureString emptypass = new SecureString();
            SecureString ssname = new SecureString();
            SecureString login = new SecureString();
            SecureString url = new SecureString();
            SecureString accountpass = new SecureString();
            SecureString masterpass;
            int rv = 0;

            masterpass = this.txtpasswordbox.SecurePassword;
            if (masterpass.Length == 0)
            {
                MessageBox.Show("Please enter Master Password to Unlock the vault: ");
                return;
            }

            if (bCreateNewVault)
            {
                rv = mgr.set_master_password(masterpass, masterpass);
                if (rv != PasswordManagerStatus.OK)
                {
                    MessageBox.Show("Setting master password failed: " + returnErrorcode(rv));
                    return;
                }          
            }            
            else
            {
                // 关键代码
                rv = mgr.vault_unlock(masterpass);
                if (rv != PasswordManagerStatus.OK)
                {
                    MessageBox.Show("Couldn't unlock vault: " + returnErrorcode(rv));
                    return;
                }
            }
            this.txtpasswordbox.Clear();
            masterpass.Clear();
            pmangerPanel.Visibility = Visibility.Collapsed;

            rv = mgr.accounts_get_count(ref count);
            if (rv != PasswordManagerStatus.OK)
            {
                MessageBox.Show("Couldn't get number of accounts from password vault: " + returnErrorcode(rv));
                return;
            }

            if (!bCreateNewVault)
            {

                for (uint i = 0; i < count; ++i)
                {

                    rv = mgr.accounts_get_info(i, ref ssname, ref login, ref url);
                    if (rv != PasswordManagerStatus.OK)
                    {
                        MessageBox.Show("Failed getting account info: " + returnErrorcode(rv));
                        return;
                    }

                    itemDetails.Add(new PasswordItemDetails(i) { ssAccount = ssname, ssURLstring = url, ssLogin = login});
                }
            }
           
            // 列表项不够8个也要用空的补够8个
            for (uint i = count; i < 8; ++i)
            {
                itemDetails.Add(new PasswordItemDetails(i));
            }

            lbPasswordList.ItemsSource = itemDetails;
            lbPasswordList.Background = SystemColors.ControlLightLightBrush;
            lbPasswordList.SelectedItem = lbPasswordList.Items.GetItemAt(0);
            btnEditAccount.IsEnabled = true;
            btnViewPassword.IsEnabled = true;
            btnCopyPassword.IsEnabled = true;
            mnCopyto.IsEnabled = true;
            btnCreateVault.IsEnabled = false;
            btnOpenVault.IsEnabled = false;
            mnOpen.IsEnabled = false;
            mnNew.IsEnabled = false;
            btnlock.IsEnabled = true;
            mnLock.IsEnabled = true;
            btnUnlock.IsEnabled = false;
            mnChangePassword.IsEnabled = true;
            this.btnViewPassword.Content = "View Password";

        }

        private void btnOpenVault_Click(object sender, RoutedEventArgs e)
        {
            OpenExistingVault();
        }

        private void OpenExistingVault()
        {
            int rv = 0;
            String path;
            SecureString randpass = new SecureString();
            SecureString emptypass = new SecureString();
            SecureString name = new SecureString();
            SecureString login = new SecureString();
            SecureString url = new SecureString();

            bCreateNewVault = false;

            OpenFileDialog openFileDialog1 = new OpenFileDialog();

            openFileDialog1.Filter = "Vault|*.vlt";
            openFileDialog1.FilterIndex = 1;

            openFileDialog1.Multiselect = true;

            // bool?是C#中的所谓Nullable Types, 该类型的变量取值可以是null
            bool? userClickedOK = openFileDialog1.ShowDialog();

            if (userClickedOK == true)
            {
                path = openFileDialog1.InitialDirectory + openFileDialog1.FileName;
                //Backup(path);               

                // 关键代码
                rv = mgr.vault_open(path);
                if (rv != PasswordManagerStatus.OK)
                {
                    MessageBox.Show("Failed opening vault file: " + returnErrorcode(rv));
                    return;
                }

                working_vault = path;
                pmangerPanel.Visibility = Visibility.Visible;
            }
        }


        private void btnCreateVault_Click(object sender, RoutedEventArgs e)
        {
            CreateNewVault();
        }

        private void CreateNewVault()
        {
            string path;
            int rv;
            SaveFileDialog dialog = new SaveFileDialog()
            {
                Filter = "Vault|*.vlt"
            };

            if (dialog.ShowDialog() == true)
            {
                path = dialog.FileName;

                if (File.Exists(path))
                {
                    File.Delete(path);
                }

                rv = mgr.vault_create(path);
                if (rv != PasswordManagerStatus.OK)
                {
                    MessageBox.Show("Failed creating vault file: " + returnErrorcode(rv));
                    return;
                }

                _txtBlock.Text = "Set a master password for the Vault file.";
                btnUnlock.Content = "Create Vault";

                bCreateNewVault = true;
                pmangerPanel.Visibility = Visibility.Visible;                
            }
        }

        private void btnEditAccount_Click(object sender, RoutedEventArgs e)
        {
             if (lbPasswordList.SelectedItem != null)
            {
                PasswordItemDetails pItem;
                int i = lbPasswordList.SelectedIndex;

                pItem = (lbPasswordList.SelectedItem as PasswordItemDetails);
                EditDialog inputDialog = new EditDialog(this, pItem);
                if (inputDialog.ShowDialog() == true)
                {
                    pItem = inputDialog.RetriveEditAccount();

                    if (pItem != null)
                    {
                        int rv = 0;

                        // The password dialog window takes care of saving the password

                        rv = mgr.accounts_set_info((uint) i, pItem.ssAccount, pItem.ssLogin, pItem.ssURLstring);
                        if (rv != PasswordManagerStatus.OK)                        
                        {
                            // Don't update the display if we can't save the new info

                            MessageBox.Show("Failed setting account info: " + returnErrorcode(rv));

                            return;
                        }

                        txtAccount.Text = pItem.Account;
                        txtURL.Text = pItem.URLstring;
                        txtLogin.Text = pItem.Login;

                        itemDetails[i].ssAccount = pItem.ssAccount;
                        itemDetails[i].ssURLstring = pItem.ssURLstring;
                        itemDetails[i].ssLogin = pItem.ssLogin;                        

                        lbPasswordList.ItemsSource = itemDetails;
                        lbPasswordList.Items.Refresh();
                    }
                }
            }
        }

        private void btnViewPassword_Click(object sender, RoutedEventArgs e)
        {
            if (lbPasswordList.SelectedItem != null)
            {
                WindowInteropHelper wih;
                Window wnd;
                IntPtr hptr;
                int rv = 0;

                wnd = Window.GetWindow(this);
                wih = new WindowInteropHelper(wnd);
                hptr = wih.Handle;

                rv= mgr.accounts_view_password((UInt32) lbPasswordList.SelectedIndex, hptr);
                if ( rv != PasswordManagerStatus.OK )
                {
                    MessageBox.Show("Couldn't view password: " + returnErrorcode(rv));
                }
            }
        }

        private void btnCopyPassword_Click(object sender, RoutedEventArgs e)
        {
            CopypasswordClipboard();
        }

        private void CopypasswordClipboard()
        {
            int rv = 0;

            if (lbPasswordList.SelectedItem != null)
            {
                int i = lbPasswordList.SelectedIndex;
                uint y = (uint)i;
                rv = mgr.accounts_password_to_clipboard(y);
                if (rv != PasswordManagerStatus.OK)
                {
                    MessageBox.Show("Failed to copy password to clipboard: " + returnErrorcode(rv));
                }
            }
        }

        private void btnlock_Click(object sender, RoutedEventArgs e)
        {
            lockVault();
        }

        private void lockVault()
        {
            lockVault(true);
        }

        private void lockVault(bool explictLock)
        {
            this.lbPasswordList.ItemsSource = null;
            this.lbPasswordList.Items.Clear();
            this.lbPasswordList.Background = SystemColors.ControlBrush;
            this.txtAccount.Text = String.Empty;
            this.txtURL.Text = String.Empty;
            this.txtLogin.Text = String.Empty;
            itemDetails.Clear();

            if (explictLock)
            {
                mgr.vault_lock();
            }

            btnEditAccount.IsEnabled = false;
            btnViewPassword.IsEnabled = false;
            btnCopyPassword.IsEnabled = false;
            mnCopyto.IsEnabled = false;
            
            // 已有保险库被解锁的情况下, 是不能 打开别的 或 创建新的 保险库的.
            btnCreateVault.IsEnabled = true;
            btnOpenVault.IsEnabled = true;
            mnOpen.IsEnabled = true;
            mnNew.IsEnabled = true;
            
            bCreateNewVault = false;
            btnlock.IsEnabled = false;
            mnLock.IsEnabled = false;
            btnUnlock.IsEnabled = true;
            pmangerPanel.Visibility = Visibility.Visible;
            this.txtpasswordbox.Clear();
            this.txtpasswordbox.Focus();

            _txtBlock.Text = "Enter your Master Password to Unlock your Vault ";
            btnUnlock.Content = "UnLock";

            mnChangePassword.IsEnabled = false;
        }

        private void ChangePassword_Click(object sender, RoutedEventArgs e)
        {
            bool? result;

            ChangePassword editPasswordDlg = new ChangePassword(this);
            result = editPasswordDlg.ShowDialog();

            // Don't print a message if the result is null (meaning the
            // user canceled or the system went to sleep).

            if ( result == true )
            {
                MessageBox.Show("Password changed");
            } else if ( editPasswordDlg.ShowDialog() == false ) {
                MessageBox.Show("Changing master password failed: " + returnErrorcode(editPasswordDlg.errorCode));                                
            }

        }

        private void Lock_Click_1(object sender, RoutedEventArgs e)
        {
            lockVault();
        }

        private void mnNew_Click(object sender, RoutedEventArgs e)
        {
            CreateNewVault();
        }

        private void mnOpen_Click(object sender, RoutedEventArgs e)
        {
            OpenExistingVault();
        }

        private void mnCopyto_Click(object sender, RoutedEventArgs e)
        {
            CopypasswordClipboard();
        }

        private void mnOptions_Click(object sender, RoutedEventArgs e)
        {
            OptionsDialog optDlg = new OptionsDialog(prefs);
            if ( optDlg.ShowDialog() == true )
            {
                prefs.SavePrefs();
                mgr.set_lock_timeout((UInt16) prefs.lockDelay);
            }
        }

        // Start listening to power change events. WPF doesn't have a nice control
        // for this.

        public void addPowerHook(HwndSourceHook hook)
        {
            hsrc.AddHook(hook);
        }

        public void removePowerHook(HwndSourceHook hook)
        {
            hsrc.RemoveHook(hook);
        }

        private void Window_SourceInitialized(object sender, EventArgs e)
        {
            // Note: This is not robust. What if hWnd changes? We really should
            // be listening for that on PropertyChanged.

            hsrc = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
            addPowerHook(new HwndSourceHook(Main_Power_Hook));
        }

        private IntPtr Main_Power_Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            UInt16 pmsg;

            // C# doesn't have definitions for power messages, so we'll get them via C++/CLI. It returns a
            // simple UInt16 that defines only the things we care about.

            pmsg= PowerManagement.message(msg, wParam, lParam);

            if ( pmsg == PowerManagementMessage.Suspend )
            {
                mgr.suspend();
                handled = true;
            } else if (pmsg == PowerManagementMessage.Resume)
            {
                int vstate = mgr.resume();

                if (vstate == ResumeVaultState.Locked) lockVault();
                handled = true;
            }

            return IntPtr.Zero;
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {            
            removePowerHook(new HwndSourceHook(this.Main_Power_Hook));
        }
    }

    public class PasswordItemDetails
    {
        public SecureString ssAccount { get; set; }        
        public SecureString ssURLstring { get; set; }
        public SecureString ssLogin { get; set; }
        public uint index { get; set; }
        public static string emptystring = "";

        public string Account { get { return (this.ssAccount == null) ? emptystring : this.ssAccount.ToInsecureString(); } set { this.ssAccount = value.ToSecureString(); } }
        public string URLstring { get { return (this.ssURLstring == null) ? emptystring : this.ssURLstring.ToInsecureString(); } set { this.ssURLstring = value.ToSecureString(); } }
        public string Login { get { return (this.ssLogin == null) ? emptystring : this.ssLogin.ToInsecureString(); } set { this.ssLogin = value.ToSecureString(); } }

        public string DisplayName { get { return String.Format("Account {0}: {1}", index, this.Account); } }

        public PasswordItemDetails (uint i)
        {
            index = i;
        }
    }
}

namespace ExtensionMethods
{
    public static class StringExtension
    {
        public static SecureString ToSecureString(this string stdString)
        {
            if (stdString == null) throw new ArgumentNullException("string stdString");

            unsafe
            {
                fixed (char* cp = stdString)
                {
                    var secureString = new SecureString(cp, stdString.Length);
                    secureString.MakeReadOnly();
                    return secureString;
                }
            }

        }
    }

    public static class SecureStringExtension
    {
        // A flower dies every time this method is called.
        public static string ToInsecureString(this SecureString ss)
        {
            if (ss == null)
                throw new ArgumentNullException("SecureString ss");

            IntPtr unmanagedString = IntPtr.Zero;
            try
            {
                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(ss);
                return Marshal.PtrToStringUni(unmanagedString);
            }
            finally
            {
                Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
            }
        }
    }
}
